package com.waypolice.manager.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.waypolice.base.BaseController;
import com.waypolice.common.utils.WorkflowUtils;
import com.waypolice.manager.component.WorkflowProcessDefinitionComponent;
import com.waypolice.manager.service.ActivitiService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * 描述:
 *
 * @author zyp
 * @description 工作流部署控制层
 * @create 2019-02-26 8:47
 */
@Controller
@Api(value = "工作流部署控制器", description = "工作流的转换模型、删除部署、导入流程")
@RequestMapping(value="/pcwindow")
public class ActDeployAction extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(ActDeployAction.class);

    @Autowired
    protected RepositoryService repositoryService;

    @Autowired
    protected RuntimeService runtimeService;

    @Autowired
    protected TaskService taskService;

    @Autowired
    ProcessEngineFactoryBean processEngine;

    @Autowired
    ProcessEngineConfiguration processEngineConfiguration;

    @Autowired
    private ActivitiService activitiService;

    @Autowired
    private WorkflowProcessDefinitionComponent workflowProcessDefinitionComponent;

    /**
     * @author:zyp
     * @description 工作流模型跳转
     * @date: 2019/2/26 9:34
     * @return 
     */
    @ApiOperation(value = "工作流模型跳转")
    @RequiresPermissions("upms:deploy:read")
    @RequestMapping(value = "/toActDeploy", method = RequestMethod.GET)
    public String toActDeploy(){
        return "/activitiManager/actDeploy";
    }

    /**
     * @author:zyp
     * @description 工作流部署列表
     * @date: 2019/2/26 9:35
     * @return 
     */
    @ApiOperation(value = "流程部署列表")
    @RequiresPermissions("upms:deploy:read")
    @RequestMapping(value="/getActDeploy")
    @ResponseBody
    public Object getActDeploy(@RequestParam(required = false, defaultValue = "0", value = "offset") int offset,
                               @RequestParam(required = false, defaultValue = "10", value = "limit") int limit,
                               @RequestParam(required = false, defaultValue = "", value = "search") String search
    ){
        Map<String, Object> result = new HashMap<String , Object>();
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().list();
        for ( ProcessDefinition processDefinition : processDefinitionList) {
            Map<String, Object> paremeterMap = new HashMap<String , Object>();
            paremeterMap.put("id",processDefinition.getId());
            paremeterMap.put("deploymentId",processDefinition.getDeploymentId());
            paremeterMap.put("name",processDefinition.getName());
            paremeterMap.put("key",processDefinition.getKey());
            paremeterMap.put("version",processDefinition.getVersion());
            paremeterMap.put("resourceName",processDefinition.getResourceName());
            paremeterMap.put("diagramResourceName",processDefinition.getDiagramResourceName());
            paremeterMap.put("suspended",processDefinition.isSuspended());
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            paremeterMap.put("deploymentTime",deployment.getDeploymentTime());
            list.add(paremeterMap);
        }
        List<Map<String, Object>>  finalList = activitiService.pagingActDeploy(list,offset,limit,search);
        result.put("rows",finalList);
        result.put("total",list.size());
        return result;
    }

    /**
     * @author zyp
     * @description 部署全部流程
     * @date 2018/3/8 11:34
     * @param
     * @return 重定向的地址
     */
    @ApiOperation(value = "部署全部流程")
    @RequestMapping(value = "/redeploy/all")
    public String redeployAll(@Value("#{APP_PROPERTIES['export.diagram.path']}") String exportDir) throws Exception {
        workflowProcessDefinitionComponent.deployAllFromClasspath(exportDir);
        return "redirect:/manage/deploy/toDeployList";
    }

    /**
     * @author:zyp
     * @description 读取资源，通过部署ID
     * @date: 2019/2/26 9:43
     * @param processDefinitionId 流程定义
     * @param resourceType        资源类型(xml|image)
     * @return
     */
    @ApiOperation(value = "通过id，读取资源")
    @RequiresPermissions("upms:deploy:read")
    @RequestMapping(value = "/readResource")
    public void loadByDeployment(@RequestParam("processDefinitionId") String processDefinitionId, @RequestParam("resourceType") String resourceType,
                                 HttpServletResponse response) throws Exception {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        String resourceName = "";
        if (resourceType.equals("image")) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (resourceType.equals("xml")) {
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
            response.getOutputStream().write(b, 0, len);
        }
    }

    /**
     * @author zyp
     * @description 挂起、激活流程实例（在部署中的挂起）
     * @date 2018/3/8 11:44
     * @param
     * @return
     */
    @ApiOperation(value = "挂起、激活流程")
    @RequiresPermissions("upms:deploy:update")
    @RequestMapping(value = "/update/{state}/{processDefinitionId}")
    @ResponseBody
    public Object updateState(@PathVariable("state") String state, @PathVariable("processDefinitionId") String processDefinitionId,
                              RedirectAttributes redirectAttributes) {
        Map<String,String> map = new HashMap<String,String>();
        if ("active".equals(state)) {
            redirectAttributes.addFlashAttribute("message", "已激活ID为[" + processDefinitionId + "]的流程定义。");
            repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
            map.put("message","已激活ID为[" + processDefinitionId + "]的流程定义。");
        } else if ("suspend".equals(state)) {
            repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
            redirectAttributes.addFlashAttribute("message", "已挂起ID为[" + processDefinitionId + "]的流程定义。");
            map.put("message","已挂起ID为[" + processDefinitionId + "]的流程定义。");
        }
        map.put("code","1");
        return  map;
    }

    /**
     * @author:zyp
     * @description 删除部署的流程，级联删除流程实例
     * @date: 2019/2/26 9:43
     * @return 
     */
    @ApiOperation(value = "删除部署的流程，级联删除流程实例")
    @RequiresPermissions("upms:deploy:delete")
    @RequestMapping(value = "/delete")
    @ResponseBody
    public String delete(@RequestParam("deploymentId") String deploymentId) {
        if (!StringUtils.isBlank(deploymentId)) {
            String[] idArray = deploymentId.split("-");
            for (String id : idArray) {
                if (StringUtils.isBlank(id)) {
                    continue;
                }
                repositoryService.deleteDeployment(id, true);
            }
        }
        return "1";
    }

    /**
     * @author:zyp
     * @description 根据部署ID转换Model
     * @date: 2019/2/26 9:42
     * @return
     */
    @ApiOperation(value = "根据部署ID转换Model")
    @RequiresPermissions("upms:deploy:changeToModel")
    @RequestMapping(value = "/changeToModel/{processDefinitionId}")
    @ResponseBody
    public List<String> convertToModel(@PathVariable("processDefinitionId")String processDefinitionId,RedirectAttributes redirectAttributes) {
        //部署失败的模型
        List<String> countFail = new ArrayList<String>();
        if (StringUtils.isBlank(processDefinitionId)) {
            return countFail;
        }else{
            String[] idArray = processDefinitionId.split("-");
            for (String id : idArray) {
                if (StringUtils.isBlank(id)) {
                    continue;
                }
                try{
                    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                            .processDefinitionId(id).singleResult();
                    InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                            processDefinition.getResourceName());
                    XMLInputFactory xif = XMLInputFactory.newInstance();
                    InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
                    XMLStreamReader xtr = xif.createXMLStreamReader(in);
                    BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

                    BpmnJsonConverter converter = new BpmnJsonConverter();
                    ObjectNode modelNode = converter.convertToJson(bpmnModel);
                    Model modelData = repositoryService.newModel();
                    modelData.setKey(processDefinition.getKey());
                    modelData.setName(processDefinition.getResourceName());
                    modelData.setCategory(processDefinition.getDeploymentId());

                    ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
                    modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
                    modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
                    modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
                    modelData.setMetaInfo(modelObjectNode.toString());

                    repositoryService.saveModel(modelData);

                    repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
                }catch (UnsupportedEncodingException ueE){
                    countFail.add(id);
                }catch(XMLStreamException xmlE){
                    countFail.add(id);
                }
            }
        }
        return countFail;
    }

    /**
     * @author:zyp
     * @description 部署项目
     * @date: 2019/2/26 9:42
     * @return 
     */
    @ApiOperation(value = "部署项目")
    @RequiresPermissions("upms:deploy:deploy")
    @RequestMapping(value = "/deploy")
    @ResponseBody
    public String deploy(/*@Value("#{APP_PROPERTIES['export.diagram.path']}") String exportDir,*/ @RequestParam(value = "file", required = false) MultipartFile file) {
        String fileName = file.getOriginalFilename();
        try {
            InputStream fileInputStream = file.getInputStream();
            Deployment deployment = null;

            String extension = FilenameUtils.getExtension(fileName);
            if (extension.equals("zip") || extension.equals("bar")) {
                ZipInputStream zip = new ZipInputStream(fileInputStream);
                deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
            } else {
                deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
            }

            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
//            for (ProcessDefinition processDefinition : list) {
//                WorkflowUtils.exportDiagramToFile(repositoryService, processDefinition, fileName);
//            }

        } catch (Exception e) {
            logger.error("error on deploy process, because of file input stream", e);
            return "0";
        }
        return "1";
    }

}